﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using HighSign.Common.Applications;
using ManagedWinapi.Windows;

namespace HighSign.UI.Forms
{
	public partial class ApplicationDefinition : Form
	{
		#region Constants

		const int HeightOpen = 456;
		const int HeightClosed = 140;

		#endregion

		#region Enumerations

		private enum SpecialItem
		{
			NewApplication = 0,
			AllApplications = 1
		}

		#endregion

		#region Constructors

		public ApplicationDefinition()
		{
			InitializeComponent();

			HideRunningApplications();

			this.KeyDown += new KeyEventHandler(ApplicationDefinition_KeyDown);
			this.lstApplications.MouseUp += new MouseEventHandler(lstApplications_MouseUp);
			this.lstApplications.KeyUp += new KeyEventHandler(lstApplications_KeyUp);
			this.cmbMatchCriteria.SelectedIndexChanged += new EventHandler(cmbMatchCriteria_SelectedIndexChanged);
			this.cmdCancel.Click += new EventHandler(cmdCancel_Click);
			this.FormClosing += new FormClosingEventHandler(ApplicationDefinition_FormClosing);
			this.Load += new EventHandler(ApplicationDefinition_Load);
			this.cmdNext.Click += new EventHandler(cmdNext_Click);
			this.cmbExistingApplication.SelectedIndexChanged += new EventHandler(cmbExistingApplication_SelectedIndexChanged);
			this.cmdRefresh.Click += new EventHandler(cmdRefresh_Click);
		}

		#endregion

		#region Events

		protected void ApplicationDefinition_Load(object sender, EventArgs e)
		{
			PopulateForm();
		}

		protected void ApplicationDefinition_FormClosing(object sender, FormClosingEventArgs e)
		{
			// User canceled dialog, re-enable gestures and hide form
			Input.MouseCapture.Instance.EnableMouseCapture();
			this.Dispose();
		}

		protected void ApplicationDefinition_KeyDown(object sender, KeyEventArgs e)
		{
			if (e.KeyCode == Keys.Escape)
				this.Close();
		}

		protected void cmdRefresh_Click(object sender, EventArgs e)
		{
			BindRunningApplications();

			// Preselect gestured application after user refreshes list
			SelectSimilar(MatchUsing.WindowClass);
		}

		protected void cmdCancel_Click(object sender, EventArgs e)
		{
			this.Close();
		}

		protected void cmdNext_Click(object sender, EventArgs e)
		{
			if (!SaveApplication())
				return;

			FormManager.Instance.ActionDefinition.Show();
			this.Close();
		}

		protected void lstApplications_KeyUp(object sender, KeyEventArgs e)
		{
			SelectSimilar((MatchUsing)cmbMatchCriteria.SelectedIndex);
		}

		protected void lstApplications_MouseUp(object sender, MouseEventArgs e)
		{
			SelectSimilar((MatchUsing)cmbMatchCriteria.SelectedIndex);
		}

		protected void cmbMatchCriteria_SelectedIndexChanged(object sender, EventArgs e)
		{
			SelectSimilar((MatchUsing)cmbMatchCriteria.SelectedIndex);
		}

		protected void cmbExistingApplication_SelectedIndexChanged(object sender, EventArgs e)
		{
			if (cmbExistingApplication.SelectedIndex == (int)SpecialItem.NewApplication)
				ShowRunningApplications();
			else
				HideRunningApplications();
		}

		#endregion

		#region Private Methods

		private bool SaveApplication()
		{
			// Did user select all applications
			if (cmbExistingApplication.SelectedIndex == (int)SpecialItem.AllApplications)
			{
				Applications.ApplicationManager.Instance.CurrentApplication = Applications.ApplicationManager.Instance.GetGlobalApplication();
				return true;
			}

			// Did user select an existing application
			if (cmbExistingApplication.SelectedIndex != (int)SpecialItem.AllApplications && cmbExistingApplication.SelectedIndex != (int)SpecialItem.NewApplication)
			{
				Applications.ApplicationManager.Instance.CurrentApplication = Applications.ApplicationManager.Instance.GetExistingUserApplication(cmbExistingApplication.SelectedItem.ToString());
				return true;
			}

			// User is creating a new application
			// Make sure we have a valid name
			if (String.IsNullOrEmpty(txtFriendlyName.Text.Trim()))
			{
				MessageBox.Show("Please provide a name for the applications you selected", "No Application Name Specified", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
				return false;
			}

			// Make sure the application name doesn't already exist
			if (Applications.ApplicationManager.Instance.ApplicationExists(txtFriendlyName.Text))
			{
				MessageBox.Show("The name you provided already exists, please provide a different name", "Name Already Exists", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
				return false;
			}

			// Find out how user wants to match other applications
			MatchUsing oMatchUsing = (MatchUsing)cmbMatchCriteria.SelectedIndex;

			// Create new application object
			Applications.UserApplication newApplication = new Applications.UserApplication();
			newApplication.MatchUsing = oMatchUsing;
			newApplication.Name = txtFriendlyName.Text.Trim();

			// Get the first selected item
			Applications.UI.ApplicationListViewItem alvFirstItem = (Applications.UI.ApplicationListViewItem)lstApplications.SelectedItems[0];

			switch (oMatchUsing)
			{
				case MatchUsing.WindowClass:
					newApplication.MatchString = alvFirstItem.WindowClass;
					break;
				case MatchUsing.WindowTitle:
					newApplication.MatchString = alvFirstItem.WindowTitle;
					break;
				case MatchUsing.ExecutableFilename:
					newApplication.MatchString = alvFirstItem.WindowFilename;
					break;
			}

			// Save new application to application list and set newly created application as selected application
			Applications.ApplicationManager.Instance.AddApplication(newApplication);
			Applications.ApplicationManager.Instance.CurrentApplication = newApplication;

			// Save entire list of applications
			Applications.ApplicationManager.Instance.SaveApplications();

			// Notify success
			return true;
		}

		private void SelectSimilar(MatchUsing MatchOptions)
		{
			// Make sure at least one item is selected
			if (lstApplications.SelectedItems.Count == 0)
			{
				txtFriendlyName.Text = "";
				return;
			}

			// Get the first selected item
			Applications.UI.ApplicationListViewItem alvFirstItem = (Applications.UI.ApplicationListViewItem)lstApplications.SelectedItems[0];

			// Select all similar applications
			foreach (ListViewItem lItem in lstApplications.Items)
			{
				Applications.UI.ApplicationListViewItem alvCurrentItem = (Applications.UI.ApplicationListViewItem)lItem;

				switch (MatchOptions)
				{
					case MatchUsing.WindowClass:
						lItem.Selected = (alvCurrentItem.WindowClass.ToLower() == alvFirstItem.WindowClass.ToLower());

						break;
					case MatchUsing.WindowTitle:
						lItem.Selected = (alvCurrentItem.WindowTitle.ToLower() == alvFirstItem.WindowTitle.ToLower());

						break;
					case MatchUsing.ExecutableFilename:
						lItem.Selected = (alvCurrentItem.WindowFilename.ToLower() == alvFirstItem.WindowFilename.ToLower());

						break;
				}
			}

			// Prepopulate application name from file description name
			txtFriendlyName.Text = alvFirstItem.ApplicationName;
		}

		private void SelectSimilar(MatchUsing MatchOptions, string MatchString)
		{
			// Select all similar applications
			foreach (ListViewItem lItem in lstApplications.Items)
			{
				Applications.UI.ApplicationListViewItem alvCurrentItem = (Applications.UI.ApplicationListViewItem)lItem;

				switch (MatchOptions)
				{
					case MatchUsing.WindowClass:
						lItem.Selected = (alvCurrentItem.WindowClass.ToLower() == MatchString.ToLower());

						break;
					case MatchUsing.WindowTitle:
						lItem.Selected = (alvCurrentItem.WindowTitle.ToLower() == MatchString.ToLower());

						break;
					case MatchUsing.ExecutableFilename:
						lItem.Selected = (alvCurrentItem.WindowFilename.ToLower() == MatchString.ToLower());

						break;
				}
			}

			// Get first selected item
			Applications.UI.ApplicationListViewItem alvFirstItem = (Applications.UI.ApplicationListViewItem)lstApplications.SelectedItems[0];

			// Prepopulate application name from file description name
			if (alvFirstItem != null)
			{
				txtFriendlyName.Text = alvFirstItem.ApplicationName;
				
				// Ensure selected item is visible
				alvFirstItem.EnsureVisible();
			}
		}

		private void BindExistingApplications()
		{
			// Clear out existing items
			cmbExistingApplication.Items.Clear();

			// Add special options
			cmbExistingApplication.Items.Add("-- New Application --");
			cmbExistingApplication.Items.Add("All Applications");

			// Populate drop down with list of saved applications
			cmbExistingApplication.Items.AddRange(Applications.ApplicationManager.Instance.GetAvailableUserApplications().Select(a => a.Name).ToArray());
		}

		private void BindRunningApplications()
		{
			lstApplications.Items.Clear();
			imgApplicationIcons.Images.Clear();
			imgApplicationIcons.TransparentColor = Color.Black;

			// Get valid running windows
			IEnumerable<SystemWindow> windows = SystemWindow.AllToplevelWindows.Where
				(
					w => w.Visible &&	// Must be a visible windows
					w.Title != "" &&	// Must have a window title
					Path.GetDirectoryName(w.Process.MainModule.FileName) != Application.StartupPath &&	// Must not be a HighSign window
					(w.ExtendedStyle & WindowExStyleFlags.TOOLWINDOW) != WindowExStyleFlags.TOOLWINDOW	// Must not be a tool window
				);

			foreach (SystemWindow sWind in windows)
			{
				Applications.UI.ApplicationListViewItem lItem = new Applications.UI.ApplicationListViewItem();

				Bitmap bMap = Icon.ExtractAssociatedIcon(sWind.Process.MainModule.FileName).ToBitmap();
				Bitmap bFinal = new Bitmap(imgApplicationIcons.ImageSize.Width, imgApplicationIcons.ImageSize.Height);
				Graphics oGraphic = Graphics.FromImage(bFinal);
				oGraphic.Clear(Color.Transparent);
				oGraphic.DrawImage(bMap, 5, 0, bFinal.Width - 10, bFinal.Height);
				imgApplicationIcons.Images.Add(bFinal);
				
				// Store identifying information
				lItem.WindowClass = sWind.ClassName;
				lItem.WindowTitle = sWind.Title;
				lItem.WindowFilename = Path.GetFileName(sWind.Process.MainModule.FileName);
				lItem.ApplicationName = sWind.Process.MainModule.FileVersionInfo.FileDescription;
				lItem.Text = sWind.Title;
				lItem.ImageIndex = imgApplicationIcons.Images.Count - 1;

				if (sWind.Title.Length > 50)
				{
					lItem.Text = sWind.Title.Substring(0, 50).Trim() + "...";
					lItem.ToolTipText = sWind.Title;
				}

				lItem.SubItems.Add(new ListViewItem.ListViewSubItem(lItem, String.Format("{0} ({1})", sWind.ClassName, Path.GetFileName(sWind.Process.MainModule.FileName))));

				lstApplications.Items.Add(lItem);
			}
		}

		private void PopulateForm()
		{
			BindExistingApplications();
			BindRunningApplications();

			cmbExistingApplication.SelectedIndex = 0;
			cmbMatchCriteria.SelectedIndex = 0;

			if (Applications.ApplicationManager.Instance.CurrentApplication == null)
			{
				// Select all applications if current gesture matches one in the global action list
				if (Applications.ApplicationManager.Instance.IsGlobalGesture(Gestures.GestureManager.Instance.GestureName))
					cmbExistingApplication.SelectedIndex = (int)SpecialItem.AllApplications;

				// Preselect gestured application incase the user wants a new application
				SelectSimilar(MatchUsing.WindowClass, Applications.ApplicationManager.Instance.CaptureWindow.ClassName);
			}
			else
				cmbExistingApplication.SelectedItem = Applications.ApplicationManager.Instance.CurrentApplication.Name;
		}

		private void ShowRunningApplications()
		{
			grpRunningApplications.Visible = true;
			this.Height = HeightOpen;
			Screen currentScreen = Screen.FromControl(this);
			this.Top = Convert.ToInt32(currentScreen.Bounds.Height / 2 - this.Height / 2);
		}

		private void HideRunningApplications()
		{
			grpRunningApplications.Visible = false;
			this.Height = HeightClosed;
			Screen currentScreen = Screen.FromControl(this);
			this.Top = Convert.ToInt32(currentScreen.Bounds.Height / 2 - this.Height / 2);
		}

		#endregion
	}
}